import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.LinkedList;

import javax.swing.JOptionPane;

/**
 * 
 */

/**
 * @author Plamen Jeliazkov
 *
 */
public class PledgeFunc {

	//breaks the pledge list into two pledge lists, one maximized, and the
	//other for scaling/removing pledges. it will print payoffs for the CURRENT round
	public static void performRound(LinkedList<Pledge> pledges, double A, double B, double alpha) {
		System.out.println("\nPLEDGES IN ORDER GIVEN");
		print(pledges);
		
		@SuppressWarnings("unchecked")
		LinkedList<Pledge> temp = (LinkedList<Pledge>) pledges.clone();
		@SuppressWarnings("unchecked")
		LinkedList<Pledge> temp2 = (LinkedList<Pledge>) pledges.clone();
		while(go(temp) == false);
		
		System.out.println("\nMAXIMIZED PLEDGES WITH REMOVAL");
		print(temp);
		
		tryToScale(temp2, temp);
		setPledgeGifts(temp2);
		
		System.out.println("\nMAXIMIZED PLEDGES IN Y-VALUE SORTED ORDER AFTER SCALING");
		System.out.println("(AND RATIO RETAINED OR UNDERCHARGED)");
		temp2 = sort(temp2);	
		print(temp2);
		
		System.out.println("\nPAYOFF FOR EACH SUCCESSFUL PLEDGE");
		printPayoff(pledges,A,B,alpha);
	}
	
	//this function will either scale or remove pledges that do not fit
	private static void tryToScale(LinkedList<Pledge> temp, LinkedList<Pledge> pledges) {
		//remove the subset of pledges from temp
		//leaving only the ones that must be scaled down to fit
		temp.removeAll(pledges);
		int count = temp.size();
		
		//SELFNOTE::TEMP contains the pledges that need to be scaled.
		//			PLEDGES contains the base of pledges that works alone.
		
		//set marker for all the pledges that need to be scaled
		//and delete any "zero" pledges
		for(int i = 0; i < temp.size(); i++)
		{
			if(temp.get(i).ratio == 0 || temp.get(i).x < .01)
			{
				System.out.println("\nPLEDGE " + temp.get(i).index + " REMOVED FOR BEING ZERO");
				temp.remove(i);
				i--;
				continue;
			}
			temp.get(i).mustscale = true;
		}
		
		System.out.println("\nPLEDGES THAT NEED TO BE SCALED");
		print(temp);
		
		//add the other pledges back in
		temp.addAll(pledges);
		
		while(count > 0)
		{
			Pledge hratio = new Pledge();
			int hratioindex = -1;
			
			//grab the pledge with the highest ratio that is next to be scaled
			for(int i = 0; i < temp.size(); i++)
			{
				if(temp.get(i).mustscale == true && temp.get(i).ratio >= hratio.ratio && temp.get(i).x > hratio.x)
				{
					hratio = temp.get(i);
					hratioindex = i;
				}
			}
			
			double sum = getSumOfOthers(temp, hratioindex);
			
			//perform the scaling
			if(sum < hratio.y)
			{
				DecimalFormat twoDForm = new DecimalFormat("#.##");
				hratio.y = Double.valueOf(twoDForm.format(sum));
				hratio.x = Double.valueOf(twoDForm.format((hratio.ratio * sum)));
				while(hratio.ratio < (hratio.x/hratio.y))
				{
					hratio.x = hratio.x - 0.01;
					System.out.println("\nREMOVED A CENT FROM PLEDGE " + hratio.index + " TO RETAIN RATIO");
				}
			}
			hratio.mustscale = false;
			
			count--;
		}
		
		//recursive check that the new scale has indeed created a working set
		if(check(temp) == false)
		{
			tryToScale(temp, pledges);
		}
	}

	//sort the pledges by Y-Value
	public static LinkedList<Pledge> sort(LinkedList<Pledge> pledges)
	{
		LinkedList<Pledge> sorted = new LinkedList<Pledge>();
		int count = pledges.size();
		
		//start with the y at the 0 index

		while(count > 0)
		{
			Pledge smallest = pledges.get(0);
			
			//search through the list for the next smallest
			for(int j = 0; j < pledges.size(); j++)
			{				
				if((pledges.get(j).y <= smallest.y && pledges.get(j).sorted == false)
						|| smallest.sorted != false)
				{
					smallest = pledges.get(j);
				}
			}
			
			//checking to make sure our smallest isnt already in the list
			if(smallest.sorted != true)
			{
				smallest.sorted = true;
				sorted.add(smallest);
			}
			
			count--;
		}
		
		return sorted;
	}
	
	//sort the pledges by index
	public static LinkedList<Pledge> sortbyIndex(LinkedList<Pledge> pledges)
	{
		LinkedList<Pledge> sorted = new LinkedList<Pledge>();
		int count = pledges.size();
		int curr = 0;
		
		System.err.println("ENTERED SORTINDEX WITH: "+count);
		//start with the y at the 0 index

		while(curr < count) {
			//search through the list for the next correct index
			for(int j = 0; j < pledges.size(); j++)
			{
				for(int i = 0; i < Server.MAX_CLIENTS; i++)
				{
					if(pledges.get(j).index == i)
					{
						System.err.println("FOUND: "+i);
						sorted.add(pledges.get(j));
						curr++;
						break;
					}
				}
			}
		}
		System.err.println("DOES THIS FINISH?");
		return sorted;
	}
	
	//prints the current set of pledges
	public static void print(LinkedList<Pledge> pledges)
	{
		for(int j = 0; j < pledges.size(); j++)
		{	
			NumberFormat formatter = new DecimalFormat("#0.00");
			System.out.println(pledges.get(j).index + ") $" + formatter.format(pledges.get(j).x) 
					+ "\t$" + formatter.format(pledges.get(j).y)
					+ "\tMONEY LEFT: $" + formatter.format(pledges.get(j).money)
					+ "\tTOTAL GIVEN: $" + formatter.format(pledges.get(j).given));
		}
	}
	
	//prints the current set of pledges' payoffs 
	//(does the setting of Pledge.payout and Pledge.othersTotalGiven)
	public static void printPayoff(LinkedList<Pledge> pledges, double A, double B, double alpha)
	{
		for(int j = 0; j < pledges.size(); j++)
		{
			NumberFormat formatter = new DecimalFormat("#0.00");
			double payoff = payoff(pledges, j, A, B, alpha);
			pledges.get(j).payout = pledges.get(j).payout + payoff;
			System.out.println(pledges.get(j).index + ") $" + formatter.format(payoff));
		}
	}
	
	//prints the current set of pledges
	public static void setPledgeGifts(LinkedList<Pledge> pledges)
	{
		for(int j = 0; j < pledges.size(); j++)
		{
			if(pledges.get(j).money < pledges.get(j).x)
			{
				System.out.println("ERROR: Gift is greater than current money for Pledge " + pledges.get(j).index + "!");
				continue;
			}
			pledges.get(j).lastgiven = pledges.get(j).x;
			pledges.get(j).money = pledges.get(j).money - pledges.get(j).x;
			pledges.get(j).given = pledges.get(j).x + pledges.get(j).given;
		}
	}
	
	//function that will check the list and WILL REMOVE the first bad pledge it encounters
	//true means a full run without ANY removals
	public static boolean go(LinkedList<Pledge> pledges)
	{
		for(int j = 0; j < pledges.size(); j++)
		{	
			if(pledges.get(j).ratio == 0 || pledges.get(j).x < .01)
			{
//				System.out.println("REMOVED " + pledges.get(j).index);
				pledges.remove(j);
				j--;
				continue;
			}
			if(!determine(pledges,j))
			{
//				System.out.println("REMOVED " + pledges.get(j).index);
				pledges.remove(j);
				return false;
			}
		}
		return true;
	}
	
	//function that will check the list return false if any removal was to be performed
	public static boolean check(LinkedList<Pledge> pledges)
	{
		if(pledges.size() == 1) return false;
		
		for(int j = 0; j < pledges.size(); j++)
		{	
			if(!determine(pledges,j))
			{
				//System.out.println("REMOVED " + j);
				return false;
			}
		}
		return true;
	}
	
	//function that tells us if the current pledge is within the set or not.
	public static boolean determine(LinkedList<Pledge> pledges, int index) {
		double sum = 0;
		Pledge current = pledges.get(index);
		
		sum = getSumOfOthers(pledges, index);
			
		//System.out.println(current.x + " " + current.y);
		//System.out.println(sum);
		
		if(current.y <= sum) return true;
		return false;
	}
	
	//function that tells us the sum of the other pledges, if index = -1, it
	//will get the gift sum of ALL the pledges.
	public static double getSumOfOthers(LinkedList<Pledge> pledges, int index) {
		double sum = 0;
		
		//get the sum of all other pledges
		for(int j = 0; j < pledges.size(); j++)
		{	
			if(j == index)
			{
				continue;
			}			
			sum = sum + pledges.get(j).x;
			
		}
		//System.out.println(current.x + " " + current.y);
		//System.out.println(sum);
		
		DecimalFormat twoDForm = new DecimalFormat("#.##");
		return Double.valueOf(twoDForm.format(sum));
	}
	
	//function tells you the payoff for a specific pledge
	public static double payoff(LinkedList<Pledge> pledges, int index, double A, double B, double alpha) {
		double payment = pledges.get(index).money;
		double G = getSumOfOthers(pledges, -1);
		pledges.get(index).othersTotalGiven = pledges.get(index).othersTotalGiven + G;
		pledges.get(index).lastAllgiven = G;
		G = Math.pow(G, alpha);
		payment = Math.pow(payment, (1-alpha));
		payment = (A*payment*G);
		return payment;
	}
	
	//function tells you the payoff prediction using a single pledge and a guessed gift of others
	public static double demoPayoff(Pledge p, double A, double B, double alpha) {
		double payment = p.money - p.x;
		double G = p.y + p.x;
		G = Math.pow(G, alpha);
		payment = Math.pow(payment, (1-alpha));
		payment = (A*payment*G);
		return payment;
	}
	
	//function tells you the payoff prediction using a single pledge and a guessed gift of others
	public static double demoPayoff(Pledge p, double assumedY, double A, double B, double alpha) {
		double payment = p.money - p.x;
		double G = assumedY + p.x;
		G = Math.pow(G, alpha);
		payment = Math.pow(payment, (1-alpha));
		payment = (A*payment*G);
		return payment;
	}
	
	public static void setupPledges(LinkedList<Pledge> pledges, int players)
	{
		String info;
		double endowment = 0;
		
		for(int i = 0; i < players; i++)
		{
			pledges.add(new Pledge());
		}
		
		for(int i = 0; i < players; i++)
		{
			info = JOptionPane.showInputDialog("What is Pledge " + i + "'s endowment?");
			try {
				endowment = new Double(info);
				if (endowment < 0) System.exit(0);
			} catch (NumberFormatException e) {
				System.err.println("Invalid ratio.");
				System.exit(0);
			}
			
			pledges.get(i).money = endowment;
		}
	}
	
	public static void preparePledges(LinkedList<Pledge> pledges)
	{
		String info;
		
		//setup UI stuffs
//		JFrame jf = new JFrame();
//		JPanel jp = new JPanel(new GridLayout(0, 1, 0, 0));
//		jp.setBackground(Color.WHITE);
//		jp.setSize(180,20);
//		JTextPane[] pane = new JTextPane[players];
//		JScrollPane js = new JScrollPane(jp);
//		js.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
//		js.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
//		jf.add(js);
//		jf.setResizable(false);
//		jf.setSize(200, 200);
		
		
//		JTextPane currentR = new JTextPane();
//		currentR.setText("ROUND " + currRound);
//		jp.add(currentR);
		
		for(int i = 0; i < pledges.size(); i++)
		{
			double ratio = 0;
			double thres = 0;
			
			do {
				info = JOptionPane.showInputDialog("What is Pledge " + i
						+ "'s ratio?");
				try {
					ratio = new Double(info);
				} catch (NumberFormatException e) {
					System.err.println("Invalid ratio.");
					System.exit(0);
				}
				info = JOptionPane.showInputDialog("What is Pledge " + i
						+ "'s threshold?");
				try {
					thres = new Double(info);
				} catch (NumberFormatException e) {
					System.err.println("Invalid threshold.");
					System.exit(0);
				}
			} while (ratio*thres > pledges.get(i).money);
			
			pledges.get(i).prepare(ratio, thres, i);
			
			//do UI stuff
//			NumberFormat formatter = new DecimalFormat("#0.00");
//			pane[i] = new JTextPane();
//			pane[i].setText(pledge.index + ") $" + formatter.format(pledge.x) 
//					+ "\t$" + formatter.format(pledge.y));
//			pane[i].setEditable(false);
//			pane[i].setSize(200,20);
//			jp.add(pane[i]);
//			jf.setVisible(true);
//			jf.repaint();
		}
//		jf.setVisible(false);
	}
}
